package service

import (
	"Go-Easy/api/model"
	. "Go-Easy/pkg/config"
	. "Go-Easy/pkg/dao"
	"Go-Easy/pkg/jwt"
	. "Go-Easy/pkg/sentinel"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"strconv"
	"strings"
)

//这是一个增效用的方法:
//执行SQL，将结果集填充到指定struct
//struct以json方式返回
func ExecSQLThenReturnResponse(c *gin.Context, SQL string, Result interface{}, Params ...interface{}) {
	if result, err := ExecSQL(SQL, Result, Params...); err == nil {
		c.JSON(200, result)
	} else {
		c.JSON(400, err.Error()) //http code:400 错误请求 — 请求中有语法问题，或不能满足请求。
	}
}

//中间件-访问API时做权限验证
func AUTH_API(c *gin.Context) {
	ReqURL := c.Request.URL
	//对于不需要权限验证的路由，直接放过
	if ReqURL.String() == "/auth/login" || strings.HasPrefix(ReqURL.String(), "/swagger/") {
		c.Next()
	} else {
		Token := c.GetHeader("Token") //从http header中获取Token属性
		//你可以在这里自定义权限验证操作，这里仅作演示
		if Token != "" {
			//在Context中加入一个KV值，可以在之后的func中用c.getXXX()方式获取.记住这个小技巧，能方便的在中间件与处理方法中间共享变量
			c.Set("Passed", "OK")
			c.Next() //执行之后的func
		} else {
			c.JSON(401, "权限验证失败") //http 401 错误: Unauthorized 未授权
			c.Abort()             //直接中止，不再执行任何之后的func
			//也可以用这种方式，更简短
			//c.AbortWithStatusJSON(401, errors.New("权限验证失败"))
		}
	}
}

//中间件-流量限制
func FlowRestrict(c *gin.Context) {
	if err := TrafficInboundRestrict("QPS2"); err != nil {
		c.AbortWithStatusJSON(503, err.Error()) //http code 503:无法获得服务—由于临时过载或维护，服务器无法处理请求
	} else {
		c.Next()
	}
}

/*
swagger注解描述 https://github.com/swaggo/swag/blob/master/README_zh-CN.md
@Summary	摘要
@Produce	API 可以产生的 MIME 类型的列表，MIME 类型你可以简单的理解为响应类型，例如：json、xml、html 等等,详细如下：
        ---Alias-------------------------MIME Type------------------------------
           json	                         application/json
           x-www-form-urlencoded	     application/x-www-form-urlencoded
           xml	                         text/xml
           plain	                     text/plain
           html	                         text/html
           mpfd	                         multipart/form-data
           json-api	                     application/vnd.api+json
           json-stream	                 application/x-json-stream
           octet-stream	                 application/octet-stream
           png	                         image/png
           jpeg	                         image/jpeg
           gif	                         image/gif
@Param	参数格式，从左到右分别为：参数名、入参类型、数据类型、是否必填、注释、example(示例)
        -入参类型有以下几种：path query header cookie  body formData
        -数据类型有 string int uint uint32 uint64 float32 bool 以及用户自定义类型(struct)
@Success	响应成功，从左到右分别为：状态码、参数类型、数据类型、注释
@Failure	响应失败，从左到右分别为：状态码、参数类型、数据类型、注释
@Router	路由，从左到右分别为：路由地址，HTTP 方法
*/

// @Summary      用户登录
// @Description  注意:获得Token后请放在http头Token属性中
// @Tags         权限验证
// @Produce      json
// @Param        Mobile  formData string  true  "手机号" example(13888888888)
// @Param        EncryptedPassword  formData string  true  "MD5加密后的密码" example(21218cca77804d2ba1922c33e0151105)
// @Success      200  {object}  model.Token
// @Failure      400  {object}  string 报错信息
// @Router       /auth/login [post]
func AUTH_Login(c *gin.Context) {
	//建议前端使用FormDate方式传参。FormDate是H5新增的一个内置对象，可以传输文件等非字符对象
	Mobile := c.PostForm("Mobile")
	EncryptedPassword := c.PostForm("EncryptedPassword")
	//定义一个空Token对象，用于填充数据。这里只有一条数据返回，如果返回的是数据集，则应该定义一个数组 []model.Token
	var m model.Token
	//直接将查询后的结果集json格式化后返回
	//演示如何直接执行SQL，并将结果集以json形式response
	//这里执行的是一个存储过程。请仔细看一下存储过程是如何写的。
	ExecSQLThenReturnResponse(c, "call sp_auth_login(?,?)", &m, Mobile, EncryptedPassword)
}

// @Summary      获取JWT加密信息
// @Description  这里演示了JWT加密字符串的获取，jwt快速入门可以参考:https://zhuanlan.zhihu.com/p/86937325
// @Tags         权限验证
// @Produce      json
// @Param        name  query  string  false  "名字" example(jack)
// @Success      200  {object}  string 加密字符串(Token)
// @Failure      400  {object}  string 报错信息
// @Router       /auth/jwt [get]
func AUTH_JWT(c *gin.Context) {
	//自定义内容
	MyContent := make(map[string]interface{})
	MyContent["Name"] = c.Query("name") //这里演示了通过url query 获取参数的方式 /auth/jwt?name=xxx

	MyClaims := jwt.GenerateMyClaims(MyContent)
	if Token, err := jwt.GenToken(MyClaims); err == nil {
		c.JSON(200, Token)
	} else {
		c.JSON(400, err.Error())
	}
}

// @Summary      JWT加密字符串解析
// @Description  这里演示了JWT加密字符串的解析,注意，会在http response header中加上续租的token”TokenRenewal“
// @Tags         权限验证
// @Produce      json
// @Param        token  path  string  false  "jwt加密字符串" example(jwt加密字符串...)
// @Success      200  {object}  jwt.MapClaims 自定义结构体(MyClaims)
// @Failure      400  {object}  string 报错信息
// @Router       /auth/jwt/parse [get]
func AUTH_JWT_Parse(c *gin.Context) {
	//这里演示一种情况:直接在api文件夹下执行swag init，会报错：cannot find type definition: jwt.MapClaims
	//因为 swag init 是在main.go同级下执行的，所有查找结构的时候只会按当前“./” 和main.go 同级查找
	//所以此时需要使用 -d 参数指定查找路径 swag init -d ./,%gopath%\pkg\mod\github.com\golang-jwt\jwt\v5@v5.0.0

	token := c.Param("token") // 使用path传参 /auth/jwt/parse/:token
	if Claims, err := jwt.ParseToken(token); err == nil {
		//Token续租,在header中带上续租后的Token
		if T, err := jwt.TokenRenewal(Claims); err == nil {
			c.Header("TokenRenewal", T)
		}

		c.JSON(200, Claims)
	} else {
		c.JSON(400, err.Error())
	}
}

// @Summary      限流演示接口
// @Description  此接口做限流演示，可尝试并发1000次观察
// @Tags         限流
// @Produce      json
// @Success      200  {object}  string 返回字符串"Hello world"
// @Failure      400  {object}  string 报错信息
// @Router       /qps/helloworld [get]
func HelloWorld(c *gin.Context) {
	c.JSON(200, "Hello world")
}

// @Summary      文件上传
// @Description  多文件上传,使用formdate传参，Key为File，Value为文件,可使用多个File key，上传多个文件
// @Tags         文件
// @Produce      json
// @Success      200  {object}  string "ok"
// @Failure      400  {object}  model.FileUploadError 自定义结构体FileUploadError
// @Router       /file/upload [post]
func FileUpload(c *gin.Context) {
	form, err := c.MultipartForm()
	if err != nil {
		c.JSON(400, err)
	}

	f := form.File["File"]             //通过字段名映射
	var errors []model.FileUploadError //错误数组

	for _, file := range f {
		//这里在每个文件名前加一个UUID前缀，以免同名文件被覆盖
		err = c.SaveUploadedFile(file, fmt.Sprintf("%s%s-%s", App.UploadPath, uuid.New().String(), file.Filename))
		if err != nil {
			errors = append(errors, model.FileUploadError{file.Filename, err.Error()})
		}
	}
	//判断错误数组中是否有记录
	if len(errors) > 0 {
		c.JSON(400, errors)
	} else {
		c.JSON(200, "ok")
	}
}

// @Summary      列出静态目录中所有文件
// @Description  
// @Tags         文件
// @Produce      json
// @Success      200  {object}  string "目录列表"
// @Failure      400  {object}  string 出错信息
// @Router       /file/static [get]
func a() {} //这是一个假func，只是因为swagger注释只能在func上扫描到

// @Summary      返回一个文件
// @Description
// @Tags         文件
// @Produce      json
// @Success      200  {object}  string "文件内容"
// @Failure      400  {object}  string 出错信息
// @Router       /file/log [get]
func b() {} //这是一个假func，只是因为swagger注释只能在func上扫描到

// @Summary      GORM使用示例1
// @Description
// @Tags         GORM
// @Produce      json
// @Success      200  {object}  []model.Auth_User_Info "目录列表"
// @Failure      400  {object}  string 出错信息
// @Router       /user/list [get]
func GORM_Example1(c *gin.Context) {
	//这里仅演示单表查询，更多信息，请参考 https://gorm.cn/zh_CN/docs/
	status, _ := strconv.Atoi(c.Query("status")) //query得到的参数都是string，这里转化为int，防止数据库隐式转换类型
	var UserList []model.Auth_User_Info
	DB.Find(&UserList, "status = ?", status)
	c.JSON(200, UserList)
}
